package com.fc.mall.portal.service.impl;

import com.fc.mall.exception.ServiceException;
import com.fc.mall.mapper.UmsIntegrationChangeHistoryMapper;
import com.fc.mall.mapper.UmsMemberSignMapper;
import com.fc.mall.model.UmsMember;
import com.fc.mall.model.UmsMemberSign;
import com.fc.mall.model.UmsMemberSignExample;
import com.fc.mall.portal.dao.PortalMemberDao;
import com.fc.mall.portal.domain.SignQueryParam;
import com.fc.mall.portal.dto.MemberSignDto;
import com.fc.mall.portal.dto.UmsMemberSignDto;
import com.fc.mall.portal.service.UmsIntegrationService;
import com.fc.mall.portal.service.UmsMemberService;
import com.fc.mall.portal.service.UmsMemberSignService;
import com.fc.mall.util.Constants;
import com.fc.mall.util.DateTimeHelper;
import ma.glasnost.orika.MapperFacade;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class UmsMemberSignServiceImpl implements UmsMemberSignService {
    public static final int INTERVAL = 7;

    @Autowired
    private PortalMemberDao portalMemberDao;

    @Autowired
    private UmsMemberSignMapper umsMemberSignMapper;

    @Autowired
    private MapperFacade mapperFacade;

    @Autowired
    private UmsIntegrationChangeHistoryMapper integrationChangeHistoryMapper;

    @Autowired
    private UmsMemberService memberService;

    @Autowired
    private UmsIntegrationService integrationService;

    @Override
    @Transactional
    public MemberSignDto sign(Long memberId) {
        final Date now = new Date();
        final int division = getDivision(now);
        int earnedPoints;
        UmsMemberSign memberSign = getMemberSign(memberId, division);
        if (memberSign != null) {
            //判断是否已经签到
            if (memberSign.getLastModifyTime().before(now) &&
                    !DateUtils.isSameDay(memberSign.getLastModifyTime(), now)) {
                int signCount = memberSign.getSignCount();
                signCount++;
                int signCont = memberSign.getSignCont();
                int missDays = daysBetween(memberSign.getLastModifyTime(), now);
                //连续签到
                signCont++;
                //非连续签到,重置1
                if (missDays > 1 || signCont > INTERVAL) {
                    signCont = 1;
                }
                //移位
                long newSign = moveByte(memberSign.getSign(), missDays);
                earnedPoints = getPointsByRule(signCont);

                UmsMemberSign newMemberSign = UmsMemberSign.builder()
                        .memberId(memberId)
                        .sign(newSign)
                        .division(division)
                        .signCont(signCont)
                        .signCount(signCount)
                        .point(earnedPoints)
                        .lastModifyTime(now)
                        .build();

                umsMemberSignMapper.insertSelective(newMemberSign);
                //签到获取积分
                integrationService.getIntegration(memberId, Constants.SourceTypeEnum.SIGN, signCont);
            } else {
                throw new ServiceException("今天已经签到过了");
            }
        } else {
            //初次签到
            earnedPoints = getInitPoints();
            memberSign = UmsMemberSign.builder()
                    .memberId(memberId)
                    .sign(1L).signCont(1).signCount(1)
                    .point(earnedPoints)
                    .division(division)
                    .lastModifyTime(now)
                    .build();
            umsMemberSignMapper.insertSelective(memberSign);
            //签到获取积分
            integrationService.getIntegration(memberId, Constants.SourceTypeEnum.SIGN, 1);
        }

        MemberSignDto memberSignDto = mapperFacade.map(memberSign, MemberSignDto.class);
        memberSignDto.setEarnedPoint(earnedPoints);

        return memberSignDto;
    }

    @Override
    public List<UmsMemberSignDto> getMemberSign(SignQueryParam signQueryParam) {
        UmsMember authMember = memberService.getCurrentMember();
        Date startDate = DateTimeHelper.getDate(
                signQueryParam.getCurrentYear(),
                signQueryParam.getCurrentMoth()-1,
                1
        );
        List<UmsMemberSign> list = getMemberSignList(authMember.getId(), getDivision(startDate));

        List<UmsMemberSignDto> signDtoList = new ArrayList<>();

        if(!list.isEmpty())
        {
            list.stream().forEach(v -> {
                signDtoList.add(new UmsMemberSignDto(DateTimeHelper.getDayFromString(v.getLastModifyTime()), v.getSignCont()));
            });
        }
        return signDtoList;
    }


    private List<UmsMemberSign> getMemberSignList(Long memberId, int division)
    {
        UmsMemberSignExample example = new UmsMemberSignExample();
        example.createCriteria()
                .andMemberIdEqualTo(memberId)
                .andDivisionEqualTo(division);
        example.setOrderByClause("last_modify_time desc");
        List<UmsMemberSign> umsMemberSigns = umsMemberSignMapper.selectByExample(example);
        return umsMemberSigns;
    }

    private UmsMemberSign getMemberSign(Long memberId, int division) {
        List<UmsMemberSign> umsMemberSigns = getMemberSignList(memberId, division);
        return umsMemberSigns.size() > 0 ? umsMemberSigns.get(0) : null;
    }

    // 连续签到送积分逻辑,以后完善
    private int getPointsByRule(int signCount) {
        return 1;
    }

    // 连续签到送积分逻辑,以后完善
    private int getInitPoints() {
        return 1;
    }

    private int getDivision(Date date) {
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        return localDate.getYear() * 100 + localDate.getMonthValue();
    }

    private int daysBetween(Date d1, Date d2) {
        return (int) ((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24));
    }

    public static long moveByte(long old, int move) {
        long moveResult = old << move;
        long result = Integer.parseInt(toBinaryString(moveResult), 2) + 1;
        return result;
    }

    private static String toBinaryString(long num) {
        return Long.toBinaryString(num);
    }
}
